import type { ActivedOptionNames, ProgramController } from '@/controller/common/Program/definition'
import { getNoopActions } from '@/controller/base/index'
import { ProgramActions, putValidOption } from '@/controller/common/Program/program'
import { ESTIMATED_TOTAL_PROGRAM_TIME, OPERATION_STATES } from '@/constants/Command'
import type { ApplianceProgram, ProgramAvailable } from '@/api/appliance'
import { cloneDeep } from 'lodash'
import type { DemoDatasource, DemoAvailableParameter } from '@/demo/types'
import { ProgramGetters } from '@/controller/common/Program/program'
import { generalUnitFormatter } from '@/pages/appliance/view/availableParameterUtils'

const DEMO_FINISH_IN_SECONDS = 30 // 30秒内结束运行程序

export function setDemoProgramState(state: ProgramController.State, demoOptions: DemoDatasource) {
  const { detail, allPrograms, available, historyPrograms } = demoOptions
  state.allPrograms = allPrograms || null
  // state.historyPrograms = historyPrograms || null
  state.currentProgramKey = detail.programKey || ''
  state.programRemaining = getDemoProgramRemaining(available, state.currentProgramKey)
}

export const DemoProgramActions: ProgramController.FragmentActions = {
  ...getNoopActions(ProgramActions),
  async setCurrentProgramGroup(value) {
    const programGroupKey = typeof value === 'string' ? value : value.key
    this.currentProgramGroupKey = programGroupKey
  },
  async setCurrentProgram(value) {
    const programKey = typeof value === 'string' ? value : value.key
    this.currentProgramKey = programKey

    // 加载available
    this.loadAvailableParameter()
  },
  async startProgramNow(program) {
    // 重置定时器
    abortRunningTimer(this as ProgramController.Store)

    // 运行
    this.operationState = OPERATION_STATES.RUN

    // 设置选中程序状态
    setTimeout(() => {
      this.setCurrentProgram(program.key)
    })

    // 设置定时器 定时终止状态
    setRunningTimer(this as ProgramController.Store)
  },
  async orderProgramNow(program) {
    // 预约
    this.operationState = OPERATION_STATES.DELAYED_START

    // 设置选中程序状态
    setTimeout(() => {
      this.setCurrentProgram(program.key)
    })
  },
  // 继续程序
  async resumeProgram() {
    this.operationState = OPERATION_STATES.RUN
    // 恢复定时器 定时终止状态
    resumeRunningTimer(this as ProgramController.Store)
  },
  // 暂停程序
  async pauseProgram() {
    this.operationState = OPERATION_STATES.PAUSE
    // 暂停定时器
    pauseRunningTimer(this as ProgramController.Store)
  },
  // 终止程序 / 取消预约
  async abortProgram() {
    this.operationState = OPERATION_STATES.READY
    // 终止定时器
    abortRunningTimer(this as ProgramController.Store)
  },
}
//
export function getDemoProgramGetters<TGetters extends ProgramController.Getters>(programGetters: TGetters): TGetters {
  return {
    ...programGetters,
    programGroups() {
      const groups = ProgramGetters.programGroups.call(this, this)
      // demo 过滤不显示收藏

      return groups.filter((group) => {
        return group.key !== 'Program.Favorite'
      })
    },
    activedOptionNames() {
      const detailOptions = this._datasource.detail?.option
      if (!Array.isArray(detailOptions)) return []

      return detailOptions.reduce<ActivedOptionNames[]>((names, option) => {
        if (this.ignoredDisplayOptionKeys.includes(option.key)) return names

        putValidOption(names, option, (this as ProgramController.Store).customDisplayOptionFormatter)
        return names
      }, [])
    },
  }
}
export function getDemoProgramActions(demoOptions: DemoDatasource): ProgramController.FragmentActions {
  const DemoAvailable = demoOptions.available

  return {
    ...DemoProgramActions,
    async loadAvailableParameter() {
      this.availableParameter = cloneDeep(DemoAvailable[this.currentProgramKey] || null)

      // 回填 Detail Options 用于调节参数 默认值
      if (this._datasource.detail) {
        this._datasource.detail.option = getDemoDetailOptions(this.availableParameter)
      }
    },
    async setCurrentProgram(program, options) {
      this.programRemaining = 0

      await DemoProgramActions.setCurrentProgram.call(this, program, options)

      // 回填程序剩余时间
      this.programRemaining = getDemoProgramRemaining(DemoAvailable, program)

      /**
       * 回填选项
       *  - 当前在运行/已预约状态时 不覆盖原有设置选项 防止Ready状态下选择过的调节参数选项被覆盖
       */
      if (
        !this._datasource.detail ||
        [OPERATION_STATES.RUN, OPERATION_STATES.DELAYED_START].includes(this.operationState as OPERATION_STATES)
      )
        return

      const currentAvailable = DemoAvailable[this.currentProgramKey]
      this._datasource.detail.option = getDemoDetailOptions(currentAvailable)

      if (Array.isArray(options) && Array.isArray(this._datasource.detail.option)) {
        for (const detailOption of this._datasource.detail.option) {
          const updateOption = options.find((option) => option.key === detailOption.key)
          if (updateOption) {
            detailOption.value = updateOption.value
          }
        }
      }
    },
  }
}

// 获取程序剩余时间
export function getDemoProgramRemaining(
  demoAvailable: DemoAvailableParameter,
  program: ApplianceProgram.Scope | string
): number {
  const programKey = typeof program === 'string' ? program : program.key
  const available = demoAvailable[programKey]
  const estimatedProgramTime = (available?.options?.find(({ key }) => key === ESTIMATED_TOTAL_PROGRAM_TIME)?.value ||
    0) as number
  return estimatedProgramTime
}

// 获取 Detail Options 默认值
export function getDemoDetailOptions(available: ProgramAvailable.Scope | null) {
  if (!available) return null
  return (
    available.options?.map((option) => {
      const demoOption = {
        key: option.key,
        name: option.name,
        unit: option.unit,
        displayvalue: option.displayvalue,
        value: (option.constraints as any)?.default,
      }

      // 数字类型直接赋值
      if (
        ['Int', 'Double'].includes(option.type) &&
        typeof (option as ProgramAvailable.NumberOption).constraints?.default === 'number'
      ) {
        demoOption.displayvalue = String((option as ProgramAvailable.NumberOption).constraints.default)
      }

      // 替换枚举类型的 displayvalue 用于显示运行中参数
      const enumConstraints = (option as ProgramAvailable.EnumOption).constraints
      if (Array.isArray(enumConstraints?.allowedvalues)) {
        const matchedIndex = enumConstraints.allowedvalues.findIndex(
          (allowedvalue) => allowedvalue === (enumConstraints as any).default
        )
        if (matchedIndex !== -1) {
          demoOption.displayvalue = enumConstraints.displayvalues[matchedIndex]
        }
      }

      return demoOption
    }) || null
  )
}

/**
 * 运行中定时器
 */
// 设置运行中定时器
type RunningTimer = {
  remaining: number
  pausing: boolean
  callback: () => any
  stop: () => void
}
const runningTimerMap = new WeakMap<object, RunningTimer>()
function setRunningTimer(store: ProgramController.Store) {
  const timerOptions: RunningTimer = {
    remaining: DEMO_FINISH_IN_SECONDS,
    pausing: false,
    callback: () => {
      if (timerOptions.pausing) return
      if (timerOptions.remaining === 0) {
        abortRunningTimer(store, OPERATION_STATES.FINISHED)
        return
      }
      timerOptions.remaining--
      console.log('当前剩余时间：', timerOptions.remaining, 's')
    },
    stop: setIntervalTimer(() => timerOptions.callback(), 1000),
  }
  runningTimerMap.set(store, timerOptions)
}
// 暂停定时器
function pauseRunningTimer(store: ProgramController.Store) {
  const timer = runningTimerMap.get(store)
  if (timer) timer.pausing = true
}
// 恢复定时器
function resumeRunningTimer(store: ProgramController.Store) {
  const timer = runningTimerMap.get(store)
  if (timer) timer.pausing = false
}
// 终止定时器
function abortRunningTimer(store: ProgramController.Store, turnToState: OPERATION_STATES = OPERATION_STATES.READY) {
  const timer = runningTimerMap.get(store)
  if (timer) {
    timer.pausing = true
    timer.stop()
    runningTimerMap.delete(store)
  }
  store.operationState = turnToState
}

function setIntervalTimer(callback: () => any, interval: number) {
  const timer = setInterval(callback, interval)
  return () => clearInterval(timer)
}
